// Audio Output
// class version by regret (ref: bsnes)

#include "burner.h"

int audSampleRate = 44100;		// sample rate
int audSegCount = 6;			// Segs in the pdsbLoop buffer
int audSegLen = 0;				// Seg length in samples (calculated from Rate/Fps)
int audAllocSegLen = 0;			// Allocated seg length in samples
bool audOkay = false;			// True if DSound was initted okay
int audDSPModule = 0;			// DSP module to use: 0 = none, 1 = low-pass filter, 2 = reverb
int audStereoUpmixing = 0;		// Enable stereo upmixing (XAudio2 only)

wchar_t audSelect[16];			// Which audio plugin is selected
static wchar_t audActive[16];	// Which audio plugin is actived
int dsDevice;					// dsound device
int oalDevice;					// openal device
int xa2Device;					// xaudio2 device


short* audNextSound = NULL;	// The next sound seg we will add to the sample loop

static InterfaceInfo audInfo = { NULL, };

// Write silence into the buffer
int audWriteSlience(int)
{
	if (audNextSound) {
		memset(audNextSound, 0, audAllocSegLen);
	}
	return 0;
}

// AudioInterface
#if defined (_WIN32)
  #include "aud_dsound.cpp"

 #ifndef NO_XAUDIO2
 #ifdef _MSC_VER
  #include "aud_xaudio2.cpp"
 #endif
 #endif
#endif

AudioInterface audioInterface;

const wchar_t* AudioInterface::driver_list()
{
	return

#if defined (_WIN32)
	L"DirectSound;"
 #ifndef NO_XAUDIO2
 #ifdef _MSC_VER
 	L"XAudio2;"
 #endif
 #endif
#endif

  L"None";
}

void AudioInterface::driver(const wchar_t* _driver)
{
	if (p) {
		term();
	}

	if (!_driver || !*_driver) {
		p = new Audio();
		return;
	}

#if defined (_WIN32)
	else if (!wcscmp(_driver, L"DirectSound")) {
		p = new AudioDS();
	}

	#ifndef NO_XAUDIO2
	#ifdef _MSC_VER
	else if (!wcscmp(_driver, L"XAudio2")) {
		p = new AudioXaudio2();
	}
	#endif
	#endif
#endif

	else {
		p = new Audio();
	}
}

void AudioInterface::term()
{
	if (p) {
		delete p;
		p = 0;
	}
}

int AudioInterface::blank()
{
	if (!audOkay) {
		return 1;
	}
	return p ? p->blank() : 1;
}

// This function checks the Sound loop, and if necessary gets some more sound
int AudioInterface::check()
{
	return p ? p->check() : 1;
}

int AudioInterface::init()
{
	if (wcscmp(audActive, audSelect)) {
		wcscpy(audActive, audSelect);
		driver(audSelect);
	}

	if (p->init()) {
		exit();
		return 1;
	}

	audOkay = true;
	return 0;
}

int AudioInterface::set(int (*callback)(int))
{
	return p ? p->set(callback) : 1;
}

int AudioInterface::play()
{
	if (!p) {
		return 1;
	}

	int ret = p->play();
	if (!ret) {
		audPlaying = true;
	}
	return ret;
}

int AudioInterface::stop()
{
	audPlaying = false;
	return p ? p->stop() : 1;
}

int AudioInterface::exit()
{
	IntInfoFree(&audInfo);

	audOkay = false;

	int ret = 0;
	if (p) {
		ret = p->exit();
	}
	return ret;
}

int AudioInterface::setvolume(int vol)
{
	return p ? p->setvolume(vol) : 1;
}

int AudioInterface::setfps()
{
	if (!p || p->setfps()) {
		exit();
		return 1;
	}
	return 0;
}

const wchar_t* AudioInterface::getName()
{
	const wchar_t* pszName = NULL;

	if (audOkay) {
		pszName = audActive;
	} else {
		pszName = audSelect;
	}

	if (pszName) {
		return pszName;
	}
	return localiseStringEx(IDS_ERR_UNKNOWN);
}

void AudioInterface::setdevice(int device)
{
#if defined (_WIN32)
	if (!wcscmp(audSelect, L"DirectSound")) {
		dsDevice = device;
	}

	#ifndef NO_XAUDIO2
	#ifdef _MSC_VER
	else if (!wcscmp(audSelect, L"XAudio2")) {
		xa2Device = device;
	}
	#endif
	#endif
#endif
}

int AudioInterface::getdevice(const wchar_t* _driver)
{
	wchar_t* driver = audSelect;
	if (_driver) {
		driver = (wchar_t*)_driver;
	}

#if defined (_WIN32)
	if (!wcscmp(driver, L"DirectSound")) {
		return dsDevice;
	}

	#ifndef NO_XAUDIO2
	#ifdef _MSC_VER
	else if (!wcscmp(driver, L"XAudio2")) {
		return xa2Device;
	}
	#endif
	#endif
#endif

	return -1;
}

InterfaceInfo* AudioInterface::get()
{
	if (IntInfoInit(&audInfo)) {
		IntInfoFree(&audInfo);
		return NULL;
	}

	if (audOkay) {
		wchar_t szString[MAX_PATH];

		audInfo.pszModuleName = getName();

		_snwprintf(szString, sizearray(szString), L"Playback at %iHz, %i%% volume", audSampleRate, audVolume / 100);
		IntInfoAddStringInterface(&audInfo, szString);

		if (audDSPModule & 1) {
			IntInfoAddStringInterface(&audInfo, L"Applying low-pass filter");
		}
		if (audDSPModule & 2) {
			IntInfoAddStringInterface(&audInfo, L"Applying reverb filter");
		}

	 	if (p) {
			p->get(&audInfo);
		}

		if (getdevice() >= 0) {
			_snwprintf(szString, sizearray(szString), L"Device: %s", audInfo.deviceName[getdevice()]);
			IntInfoAddStringInterface(&audInfo, szString);
		}
	} else {
		IntInfoAddStringInterface(&audInfo, L"Audio not initialized");
	}

	return &audInfo;
}

int AudioInterface::select(const wchar_t* _driver)
{
	if (!_driver || !*_driver) {
		return 1;
	}

	wcscpy(audSelect, _driver);
	return 0;
}
